/*
 * @Author: dadadaXU 1413107032@qq.com
 * @Date: 2024-10-10 15:02:34
 * @LastEditors: dadadaXU 1413107032@qq.com
 * @LastEditTime: 2025-01-05 09:39:49
 * @FilePath: /FileBackup/src/argumentsParser.cpp
 * @Description: 用户命令解析实现
 */
#include "../include/argumentsParser.hpp"

/// @brief 使用第三方库[cxxopts]分解用户输入参数
CArgumentsParser::CArgumentsParser() : options("fbk", "数据备份软件")
{
    /* 基础功能 */
    options.set_width(SHOW_HELP_WIDTH).add_options()
        ("b,backup", "数据备份", cxxopts::value<bool>(userFlag.backup))
        ("r,restore", "数据恢复", cxxopts::value<bool>(userFlag.restore))
        ("v,view", "查看备份文件信息", cxxopts::value<std::filesystem::path>(viewInfoPath))
        ("i,input", "输入文件路径", cxxopts::value<std::filesystem::path>(inputPath))
        ("o,output", "输出文件路径", cxxopts::value<std::filesystem::path>(outputPath))
        ("h,help", "显示此帮助文档", cxxopts::value<bool>(userFlag.help));

    /* 数据备份 */
    options.set_width(SHOW_HELP_WIDTH).add_options("Backup")
        ("c,compress", "对打包文件压缩", cxxopts::value<bool>(userFlag.compress))
        ("path", "指定过滤路径（正则表达式）", cxxopts::value<std::string>(fileFilterArguments.path))
        ("type", "过滤文件类型（可以多个组合）\n `-`普通文件 `D`目录文件 `P`管道文件 `S`符号链接 `H`硬链接", cxxopts::value<std::string>(fileTypes))
        ("name", "过滤文件名（正则表达式）", cxxopts::value<std::string>(fileFilterArguments.name))
        ("size", "过滤文件大小区间(bytes) 例 \"0 32768\"", cxxopts::value<std::string>(fileSize))
        ("atime", "文件访问时间区间\n例 \"1970-01-01 00:00:00 2024-12-31 23:59:59\"", cxxopts::value<std::string>(atime))
        ("mtime", "文件修改时间区间\n例 \"1970-01-01 00:00:00 2024-12-31 23:59:59\"", cxxopts::value<std::string>(mtime))
        ("ctime", "文件改变时间区间\n例 \"1970-01-01 00:00:00 2024-12-31 23:59:59\"", cxxopts::value<std::string>(ctime))
        ("m,message", "添加备注信息", cxxopts::value<std::string>(message));

    /* 数据还原 */
    options.set_width(SHOW_HELP_WIDTH).add_options("Restore")
        ("d,delete", "删除解包原始文件 or 压缩前的打包文件", cxxopts::value<bool>(userFlag.deleteFile))
        ("a,metadata", "恢复文件的元数据", cxxopts::value<bool>(userFlag.metadata))
        ("g,goback", "按照原始路径恢复文件", cxxopts::value<bool>(userFlag.goBack));
}

CArgumentsParser::~CArgumentsParser()
{
}

bool CArgumentsParser::Parse(int argc, char **argv)
{
    try
    {
        result = options.parse(argc, argv);
    }
    catch (const cxxopts::exceptions::exception &e)
    {
        /* 使用cxxopts解析用户参数失败 */
        std::cerr << e.what() << std::endl;
        return false;
    }

    /* 设定信息判断标识 */
    if (userFlag.help)
    {
        std::cout << options.help() << std::endl;
        return true;
    }

    /* 补充含参用户输入标志 */
    userFlag.viewInfo = result.count("view");
    userFlag.input = result.count("input");
    userFlag.output = result.count("output");
    userFlag.filterPath = result.count("path");
    userFlag.filterType = result.count("type");
    userFlag.filterName = result.count("name");
    userFlag.filterSize = result.count("size");
    userFlag.atime = result.count("atime");
    userFlag.mtime = result.count("mtime");
    userFlag.ctime = result.count("ctime");
    userFlag.message = result.count("message");

    // TODO 参数互斥关系检查
    bool arguments_check = true;
    if (userFlag.backup)
        arguments_check &= CheckBackupArguments();
    if (userFlag.restore)
        arguments_check &= CheckRestoreArguments();
    if (userFlag.viewInfo)
        arguments_check &= CheckViewInfoArguments();

    return arguments_check;
}

/** 检查数据备份参数
 * 0. 命令参数检查：
 * - 不包含：数据恢复、查看备份文件信息、恢复文件的元数据
 * 1. 路径检查
 * 2. 文件加密
 * 3. 文件过滤参数检查
 * 4. 文件备注信息长度
 */
bool CArgumentsParser::CheckBackupArguments()
{
    /* 命令参数检查 */
    if (userFlag.restore || userFlag.viewInfo || userFlag.metadata || userFlag.goBack)
    {
        std::cerr << "Too many arguments for BackupArguments!" << std::endl;
        return false;
    }

    /* 路径检查 */
    if (!CheckFilePath())
        return false;

    /* 文件过滤参数 */
    fileFilterArguments.mode &= 0;
    if (userFlag.filterPath)
    {
        if (!CheckRegexExpression(fileFilterArguments.path))
            return false;
        fileFilterArguments.mode |= FILE_FILTER_PATH;
    }
    if (userFlag.filterType)
    {
        if (!CheckFileType())
            return false;
        fileFilterArguments.mode |= FILE_FILTER_TYPE;
    }
    if (userFlag.filterName)
    {
        if (!CheckRegexExpression(fileFilterArguments.name))
            return false;
        fileFilterArguments.mode |= FILE_FILTER_NAME;
    }
    if (userFlag.filterSize)
    {
        if (!CheckFileSize())
            return false;
        fileFilterArguments.mode |= FILE_FILTER_SIZE;
    }
    if (userFlag.atime)
    {
        if (!CheckFileTime(atime, fileFilterArguments.atime_from, fileFilterArguments.atime_to))
            return false;
        fileFilterArguments.mode |= FILE_FILTER_ATIME;
    }
    if (userFlag.mtime)
    {
        if (!CheckFileTime(mtime, fileFilterArguments.mtime_from, fileFilterArguments.mtime_to))
            return false;
        fileFilterArguments.mode |= FILE_FILTER_MTIME;
    }
    if (userFlag.ctime)
    {
        if (!CheckFileTime(ctime, fileFilterArguments.ctime_from, fileFilterArguments.ctime_to))
            return false;
        fileFilterArguments.mode |= FILE_FILTER_CTIME;
    }

    /* 文件备注信息长度 */
    if (userFlag.message)
    {
        if (message.length() > PAK_MESSAGE_LEN)
        {
            std::cerr << "Message too long! (Max length: " << PAK_MESSAGE_LEN << " )" << std::endl;
            return false;
        }
    }

    return true;
}

/** 检查数据还原参数
 * 0. 命令参数检查：
 * - 不包含：数据备份、查看备份文件信息、文件压缩、文件加密、过滤路径、过滤文件类型、过滤文件名称、过滤文件大小、过滤时间、备注信息
 * 1. 路径检查
 * 2. 检查文件类型（后缀名）
 */
bool CArgumentsParser::CheckRestoreArguments()
{
    /* 命令参数检查 */
    if (userFlag.backup || userFlag.viewInfo || 
        userFlag.compress || 
        userFlag.filterPath || userFlag.filterType || userFlag.filterName || userFlag.filterSize || 
        userFlag.atime || userFlag.ctime || userFlag.mtime || userFlag.message)
    {
        std::cerr << "Too many arguments for RestoreArguments!" << std::endl;
        return false;
    }

    /* 路径检查 */
    if (!CheckFilePath())
        return false;

    /* 检查文件类型（后缀名）*/
    std::string suffix = inputPath.extension();
    if (suffix != FILE_SUFFIX_PACK && suffix != FILE_SUFFIX_COMPRESS)
    {
        std::cerr << "Unsupport file type: " << inputPath << std::endl;
        return false;
    }

    return true;
}

/** 查看数据备份参数
 * 0. 命令参数检查：
 * - 不包含：数据备份、数据恢复
 * 1. 路径检查
 * 2. 检查文件类型（后缀名）
 */
bool CArgumentsParser::CheckViewInfoArguments()
{
    /* 命令参数检查 */
    if (userFlag.backup || userFlag.compress || 
        userFlag.filterPath || userFlag.filterType || userFlag.filterName || userFlag.filterSize || 
        userFlag.atime || userFlag.ctime || userFlag.mtime || userFlag.message ||
        userFlag.restore || userFlag.metadata || userFlag.deleteFile || userFlag.goBack)
    {
        std::cerr << "Too many arguments for ViewInfo!" << std::endl;
        return false;
    }

    /* 路径检查 */
    if (!std::filesystem::exists(viewInfoPath))
    {
        std::cerr << "Invalid file path: " << viewInfoPath << std::endl;
        return false;
    }

    /* 检查文件类型（后缀名）*/
    std::string suffix = viewInfoPath.extension();
    if (suffix != FILE_SUFFIX_PACK && suffix != FILE_SUFFIX_COMPRESS)
    {
        std::cerr << "Unsupport file type: " << viewInfoPath << std::endl;
        return false;
    }

    return true;
}

bool CArgumentsParser::CheckRegexExpression(std::string expression)
{
    try
    {
        std::regex reg(expression);
    }
    catch (const std::exception &e)
    {
        std::cerr << "Invalid regex expression: " << e.what() << std::endl;
        return false;
    }

    return true;
}

bool CArgumentsParser::CheckFilePath()
{
    if (!userFlag.input)
    {
        std::cerr << "Input file path missing!" << std::endl
                  << "use \"-i,--input <file path>\" to add input file" << std::endl;
        return false;
    }
    else if (!std::filesystem::exists(inputPath))
    {
        std::cerr << "Invalid input file path: " << inputPath << std::endl;
        return false;
    }

    if (!userFlag.output)
    {
        /* 用户没有指定，默认为当前工作目录 */
        outputPath = std::filesystem::current_path();
    }
    // else if (!std::filesystem::is_directory(outputPath))
    // {
    //     std::cerr << "Invalid output file path: " << outputPath << std::endl;
    //     return false;
    // }

    return true;
}

bool CArgumentsParser::CheckFileType()
{
    fileFilterArguments.types &= 0;
    for (auto c : fileTypes)
    {
        switch (c)
        {
        case '-':
            fileFilterArguments.types |= FILE_TYPE_NORMAL;
            break;
        case 'D':
            fileFilterArguments.types |= FILE_TYPE_DIRECTORY;
            break;
        case 'P':
            fileFilterArguments.types |= FILE_TYPE_PIPE;
            break;
        case 'S':
            fileFilterArguments.types |= FILE_TYPE_SYMBOLIC_LINK;
            break;
        case 'H':
            fileFilterArguments.types |= FILE_TYPE_HARD_LINK;
            break;
        default:
            std::cerr << "Invalid file type: " << c << std::endl;
            return false;
            break;
        }
    }

    return true;
}

bool CArgumentsParser::CheckFileSize()
{
    std::stringstream ss(fileSize);
    int from, to;

    /* 数据格式转化 */
    if (ss >> from >> to)
    {
        // 确保 size_from 小于等于 size_to
        if (from < 0 || to < 0)
        {
            std::cerr << "Invalid values: " << from << " " << to << std::endl;
            return false;
        }
        if (from > to) // 确保 from <= to
        {
            from = from + to;
            to = from - to;
            from = from - to;
        }
        fileFilterArguments.size_from = static_cast<off_t>(from);
        fileFilterArguments.size_to = static_cast<off_t>(to);
    }
    else
    {
        std::cerr << "Failed to convert `string` to `int` values" << std::endl;
        return false;
    }

    return true;
}

bool CArgumentsParser::CheckFileTime(std::string dateTimeStr, std::time_t &dataTimeFrom, std::time_t &dataTimeTo)
{
    std::tm time = {};
    std::istringstream iss(dateTimeStr);

    /* 起始时间 */
    if (iss >> std::get_time(&time, "%Y-%m-%d %H:%M:%S"))
    {
        dataTimeFrom = std::mktime(&time);
        if (dataTimeFrom == -1)
        {
            std::cerr << "Invalid time:" << dateTimeStr << std::endl;
            return false;
        }
    }
    else
    {
        std::cerr << "Time format: \"%Y-%m-%d %H:%M:%S\" !" << std::endl;
        return false;
    }

    /* 结束时间 */
    if (iss >> std::get_time(&time, "%Y-%m-%d %H:%M:%S"))
    {
        dataTimeTo = std::mktime(&time);
        if (dataTimeTo == -1)
        {
            std::cerr << "Invalid time:" << dateTimeStr << std::endl;
            return false;
        }
    }
    else
    {
        std::cerr << "Time format: \"%Y-%m-%d %H:%M:%S\" !" << std::endl;
        return false;
    }

    /* 保证时间大小关系 */
    if (dataTimeFrom > dataTimeTo) 
    {
        time_t temp = dataTimeFrom;
        dataTimeFrom = dataTimeTo;
        dataTimeTo = temp;
    }

    return true;
}